/*
 * 
 * LegendShop 多用户商城系统
 * 
 *  版权所有,并保留所有权利。
 * 
 */
package com.legendshop.oa.security;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.util.StringUtils;

import com.legendshop.oa.handler.RequestHolder;
import com.legendshop.util.IPHelper;

/**
 * 记住我服务.
 */
public class IPTokenBasedRememberMeServices extends TokenBasedRememberMeServices {
	
	/** The log. */
	private final static Logger log = LoggerFactory.getLogger(IPTokenBasedRememberMeServices.class);

	/** The request cache. */
	private RequestCache requestCache;
	
	private boolean ignoreIP; //是否忽略IP的影响
	
	/**
	 * Instantiates a new iP token based remember me services.
	 *
	 * @param key the key
	 * @param userDetailsService the user details service
	 */
	public IPTokenBasedRememberMeServices(String key, UserDetailsService userDetailsService) {
		super(key, userDetailsService);
	}
	
	/**
	 * 验证成功之后执行remember me功能.
	 *
	 * @param request the request
	 * @param response the response
	 * @param successfulAuthentication the successful authentication
	 */
	 @Override
	    public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
	            Authentication successfulAuthentication) {
		 
		 	UserDetail userDetail = getUserDetail(successfulAuthentication);
		 	
	        String username = userDetail.getUsername();
	        String password = userDetail.getPassword();

	        // If unable to find a username and password, just abort as TokenBasedRememberMeServices is
	        // unable to construct a valid token in this case.
	        if (!StringUtils.hasLength(username)) {
	            logger.debug("Unable to retrieve username");
	            return;
	        }

	        if (!StringUtils.hasLength(password)) {
	            UserDetails user = getUserDetailsService().loadUserByUsername(username);
	            password = user.getPassword();

	            if (!StringUtils.hasLength(password)) {
	                logger.debug("Unable to obtain password for user: " + username);
	                return;
	            }
	        }

	        int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
	        long expiryTime = System.currentTimeMillis();
	        // SEC-949
	        expiryTime += 1000L* (tokenLifetime < 0 ? TWO_WEEKS_S : tokenLifetime);

	        String signatureValue = makeTokenSignature(expiryTime, username, password);

	        setCookie(new String[] {username, Long.toString(expiryTime), signatureValue}, tokenLifetime, request, response);

	        if (logger.isDebugEnabled()) {
	            logger.debug("Added remember-me cookie for user '" + username + "', expiry: '"
	                    + new Date(expiryTime) + "'");
	        }
	    }
	 
	 /**
 	 * Gets the user detail.
 	 *
 	 * @param successfulAuthentication the successful authentication
 	 * @return the user detail
 	 */
 	private UserDetail getUserDetail(Authentication successfulAuthentication){
		 return (UserDetail)successfulAuthentication.getPrincipal();
	 }

	/**
	 * Calculates the digital signature to be put in the cookie. Default value
	 * is MD5 ("username:tokenExpiryTime:password:key")
	 *
	 * @param tokenExpiryTime the token expiry time
	 * @param username the username
	 * @param password the password
	 * @return the string
	 */
	protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
		String data = new StringBuffer()
		        .append(username).append(":")
		        .append(tokenExpiryTime).append(":")
				.append(password).append(":")
				.append(getKey()).append(":")
				.append(getLoginIp(RequestHolder.getRequest())).toString();
		MessageDigest digest;
		try {
			digest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException("No MD5 algorithm available!");
		}

		return new String(Hex.encode(digest.digest(data.getBytes())));
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices#processAutoLoginCookie(java.lang.String[], javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
			HttpServletResponse response) {
		//记住上一个页面
		requestCache.saveRequest(request, response);

		// take off the last token
		String ipAddressToken = cookieTokens[cookieTokens.length - 1];
		if (!getLoginIp(request).equals(ipAddressToken)) {
			throw new InvalidCookieException("Cookie IP Address did not  contain a matching IP (contained '"
					+ ipAddressToken + "')");
		}
		UserDetails userDetails = null;
		try {
			userDetails =   super.processAutoLoginCookie(Arrays.copyOf(cookieTokens, cookieTokens.length - 1), request, response);
			return userDetails;
		} catch (Exception e) {
			log.error("",e);
		}
		if(userDetails == null){
			log.warn("Can not load user, use default user");
			userDetails = new UserDetails() {
	
				private static final long serialVersionUID = 8283524610144629624L;

				@Override
				public boolean isEnabled() {
					return false;
				}
				
				@Override
				public boolean isCredentialsNonExpired() {
					// TODO Auto-generated method stub
					return false;
				}
				
				@Override
				public boolean isAccountNonLocked() {
					// TODO Auto-generated method stub
					return false;
				}
				
				@Override
				public boolean isAccountNonExpired() {
					// TODO Auto-generated method stub
					return false;
				}
				
				@Override
				public String getUsername() {
					return "RememberUser";
				}
				
				@Override
				public String getPassword() {
					// TODO Auto-generated method stub
					return null;
				}
				
				@Override
				public Collection<? extends GrantedAuthority> getAuthorities() {
					return null;
				}
			};
		}
		
		
		return userDetails;

	}

	/* (non-Javadoc)
	 * @see org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices#setCookie(java.lang.String[], int, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	protected void setCookie(String[] tokens, int maxAge, HttpServletRequest request, HttpServletResponse response) {
		// append the IP adddress to the cookie
		String[] tokensWithIPAddress = Arrays.copyOf(tokens, tokens.length + 1);
		//tokensWithIPAddress[tokensWithIPAddress.length - 1] = IPHelper.getIpAddr(request);
		tokensWithIPAddress[tokensWithIPAddress.length - 1] = getLoginIp(request);
		super.setCookie(tokensWithIPAddress, maxAge, request, response);
		
		//set for non https
		super.setUseSecureCookie(false);
	}

	/**
	 * Sets the request cache.
	 *
	 * @param requestCache the new request cache
	 */
	public void setRequestCache(RequestCache requestCache) {
		this.requestCache = requestCache;
	}
	
	private String getLoginIp(HttpServletRequest request){
		if(ignoreIP){
			return "MobileIP";
		}else{
			return IPHelper.getIpAddr(request);
		}
	}

	public void setIgnoreIP(boolean ignoreIP) {
		this.ignoreIP = ignoreIP;
	}

}
